/*
  The pic.c file handles the PIC - "Programmable Interrupt Controller" on an Intel 386 like system.
  The PIC is the chip called 8259
*/

#include <types.h>
#include <klib.h>
#include <panic.h>
#include <pic.h>
#include <interrupts.h>


//Define EXTERN to nothing since we'll implement the functions here

/* Useful defines to handle IRQs */

#define IRQ_ALL_ON             (byte_t)0xFF
#define IRQ_ALL_OFF            (byte_t)0x00


// This mask: 0=disabled ; 1=enabled
volatile byte_t MasterMask,SlaveMask;              /* Keep track of Interrupt Masks */
/*  Masking:
    For PIC: 0=serve ; 1=mask
*/



/* Private functions prototypes */

void mask_irqs(byte_t master,byte_t slave);


/* Public functions implementation */
 

void reprogram_PICs(void)
  {	
    /*
     Reprogram the PICs (8259 Master and Slave)
     So that we tell the PIC that we have 8 Bytes long interrupt vectors
     
     NOTE: The following code tells the PICs to map IRQs in the following manner:
     IRQs 00 - 07: INTs 0x30 - 0x37
     IRQs 08 - 15: INTs 0x38 - 0x3F
    */

    /* Send ICW1 */
    outb(PIC_8259M_CTRL,0x11);            /* Need ICW4 */
    outb(PIC_8259S_CTRL,0x11);            /* Need ICW4 */

    /* Send ICW2 */
    outb(PIC_8259M_MASK,IRQ00_VECTOR);      /* INT# to serve IRQ 0 */
    outb(PIC_8259S_MASK,IRQ08_VECTOR);      /* INT# to serve IRQ 8 */
    
    /* Send ICW3 */
    outb(PIC_8259M_MASK,0x04);            /* Master PIC has a slave on IRQ 2 */
    outb(PIC_8259S_MASK,0x02);            /* Slave requests will come from IRQ 2 (0x04 ???) */
    
    /* Send ICW4 */
    ///// this is with auto EOI
    //    outb(PIC_8259M_MASK,0x03);            /* 80C86/80C88 Mode (not MCS) (0x01) */
    //    outb(PIC_8259S_MASK,0x03);            /* same as above */


    /* Send ICW4 */ // 0x01
    outb(PIC_8259M_MASK,0x01);            /* 80C86/80C88 Mode (not MCS) (0x01) */
    outb(PIC_8259S_MASK,0x01);            /* same as above */

    /* Disable all irqs for now */
    irq_disable_all();                    /* Sends OCW1 implicitly and initializes masks */

    /* Send OCW2 */
    //outb(PIC_8259S_CTRL,IRQ_EOI);         /* Send non-specific EOI command */

    /* Send OCW2 */                       // Warning MS -> SM (was)
    // outb(PIC_8259M_CTRL,IRQ_EOI);         /* Send non-specific EOI command */
    // outb(PIC_8259S_CTRL,IRQ_EOI);         /* Send non-specific EOI command */ 
  }


void irq_enable(int n)
  {
    //printf(" *** STI ***\n");

    if (n<0 || n>=IRQ_NUM)
      panic("Enabling invalid IRQ.");

    if (n<IRQ_NUM/2)
      {
	//Use Master PIC	
	//Set appropriate bit in the Master mask  
	MasterMask|=(byte_t)1<<n;	
      }
    else
      {	
	//Use Slave PIC
	//Set appropriate bit in the Slave mask  
	n-=IRQ_NUM/2;
	SlaveMask|=(byte_t)1<<n;
      }
    //Send command
    mask_irqs(MasterMask,SlaveMask);    
  }


void irq_disable(int n)
  {
    //printf(" *** CLI ***\n");

    if (n<0 || n>=IRQ_NUM)
      panic("Disabling invalid IRQ.");

    if (n<IRQ_NUM/2)
      {
	//Use Master PIC	
	//Set appropriate bit in the Master mask  
	MasterMask&=~(byte_t)(1<<n);	
      }
    else
      {
	//Use Slave PIC
	//Set appropriate bit in the Slave mask  
	n-=IRQ_NUM/2;
	SlaveMask&=~(byte_t)(1<<n);
      }
    //Send command
    mask_irqs(MasterMask,SlaveMask);
  }


void irq_enable_all(void)
  {
    //Set Masks properly
    MasterMask=IRQ_ALL_ON;
    SlaveMask=IRQ_ALL_ON;
    //Send command
    mask_irqs(MasterMask,SlaveMask);    
  }


void irq_disable_all(void)
  {
    //Set Masks properly
    MasterMask=IRQ_ALL_OFF;
    SlaveMask=IRQ_ALL_OFF;
    //Send command
    mask_irqs(MasterMask,SlaveMask);
  }

/* send specific EOI */
void send_specific_EOI(byte_t irq_num)
  {
    byte_t pic;
    byte_t num;

    if (irq_num<0 || irq_num>=IRQ_NUM)
      panic("Asking to send EOI for invalid IRQ.");

    if (irq_num<IRQ_NUM/2)
      {
	pic = PIC_8259M_CTRL;  // use master
      }
    else
      {
	pic = PIC_8259S_CTRL;  // use slave
	irq_num-=IRQ_NUM/2;
      }

    // 0x60 is the specific EOI command for OCW2
    //num = 0x60 | ((byte_t)(1<<irq_num));
    num = 0x60 | (irq_num&0x03);

    /* Send OCW2 */                       
    outb(pic,num);     // specific EOI
  }

/* send non-specific EOI */
void send_EOI(void)
  {
    /* Send OCW2 */                       
    outb(PIC_8259M_CTRL,IRQ_EOI);         /* Send non-specific EOI command */
    outb(PIC_8259S_CTRL,IRQ_EOI);         /* Send non-specific EOI command */     
  }

/* determine if IRQ is spurious */
int spurious_IRQ(int irq_num)
  {
    byte_t n;

    if (irq_num<0 || irq_num>=IRQ_NUM)
      panic("Determining spuriousness of unknown IRQ.");

    if (irq_num<IRQ_NUM/2)
      {
	n=(byte_t)(1<<irq_num);

	if (!(MasterMask & n))
	  return 1;
      }
    else
      {
	irq_num-=IRQ_NUM/2;
	n=(byte_t)(1<<irq_num);

	if (!(SlaveMask & n))
	  return 1;
      }

    return 0;
  }


/* Private functions implementation */

void mask_irqs(byte_t master,byte_t slave)
  {
    //printf("Master = %x  Slave = %x\n",(int)master,(int)slave);

    /* Send OCW1 */
    outb(PIC_8259M_MASK,~master);         /* Master PIC IRQs Masking */
    outb(PIC_8259S_MASK,~slave);          /* Slave PIC IRQs Masking */    
  }
